/** 
 * Title: ast.h
 * Description: Abstract Syntax Tree for Pascal-S
 * Copyright: Copyright (c) 2022
 * Company: Beijing University of Posts and Telecommunications
 * @author 2019211410 王敏行 2019211416 邹宇江
 * @date 2022/04/14 11:18 GMT+08:00
 * @version 0.9.0-beta
 * Reference: http://www.csci.csusb.edu/dick/samples/pascal.syntax.html
 */

#ifndef _AST_H
#define _AST_H

#include <string>
#include <vector>

#include "ast.decl.h"
#include "../visitor/visitor.h"

/// Base
class AST {
public:
    AST() = default;
    ~AST() = default;
    void set_loc(int ln, int col) {
        this->ln = ln;
        this->col = col;
    }
    int get_ln() { return ln; }
    int get_col() { return col; }
    virtual void* accept(Visitor* visitor) = 0;
private:
    int ln;
    int col;
};

/// Programs and Blocks -----------------------------------

class ProgramAST : public AST {
public:
    ProgramAST() {}
    ProgramAST(ProgramHeadingAST* head, ProgramBlockAST* block)
        : head(head), block(block) {}
    ProgramHeadingAST* get_head() { return head; }
    ProgramBlockAST* get_block() { return block; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    ProgramHeadingAST* head;
    ProgramBlockAST* block;
};

class ProgramHeadingAST : public AST {
public:
    ProgramHeadingAST() {}
    ProgramHeadingAST(std::string name, ProgramParametersAST* params)
        : name(name), params(params) {}
    std::string get_name() { return name; }
    ProgramParametersAST* get_params() { return params; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    std::string name;
    ProgramParametersAST* params;
};

class ProgramBlockAST : public AST {
public:
    ProgramBlockAST() {}
    ProgramBlockAST(BlockAST* block) : block(block) {}
    BlockAST* get_block() { return block; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    BlockAST* block;
};

class BlockAST : public AST {
public:
    BlockAST() {}
    BlockAST(ConstantDefinitionListAST* constList,
        TypeDefinitionListAST* typeList,
        VariableDeclarationListAST* varList,
        ProcedureFunctionDeclarationListAST* procFuncList,
        StatementPartAST* stmt)
        : constList(constList), typeList(typeList),
        varList(varList), procFuncList(procFuncList), stmt(stmt) {}
    ConstantDefinitionListAST* get_const() { return constList; }
    TypeDefinitionListAST* get_type() { return typeList; }
    VariableDeclarationListAST* get_var() { return varList; }
    ProcedureFunctionDeclarationListAST* get_proc() { return procFuncList; }
    StatementPartAST* get_stmt() { return stmt; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    ConstantDefinitionListAST* constList;
    TypeDefinitionListAST* typeList;
    VariableDeclarationListAST* varList;
    ProcedureFunctionDeclarationListAST* procFuncList;
    StatementPartAST* stmt;
};

class ProgramParametersAST : public AST {
public:
    ProgramParametersAST() {}
    ProgramParametersAST(IdentifierListAST* idList) : idList(idList) {}
    IdentifierListAST* get_id_list() { return idList; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    IdentifierListAST* idList;
};

class IdentifierListAST : public AST {
public:
    IdentifierListAST() {}
    void push_back(std::string id) { idList.push_back(id); }
    std::vector<std::string> get_id_list() { return idList; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    std::vector<std::string> idList;
};

/// Declarations and Definitions --------------------------

// Not support in Pascal-S
// class LabelDeclarationsAST : public AST {}
// class LabelListAST : public AST {}
// class LabelAST : public AST {}
// class DigitSequenceAST : public AST {}

class ConstantDefinitionListAST : public AST {
public:
    ConstantDefinitionListAST() {}
    void push_back(ConstantDefinitionAST* constDef) {
        constList.push_back(constDef);
    }
    std::vector<ConstantDefinitionAST*> get_const_list() { return constList; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    std::vector<ConstantDefinitionAST*> constList;
};

class ConstantDefinitionAST : public AST {
public:
    ConstantDefinitionAST() {}
    ConstantDefinitionAST(std::string id, ConstantAST* constVal)
        : id(id), constVal(constVal) {}
    std::string get_id() { return id; }
    ConstantAST* get_val() { return constVal; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    std::string id;
    ConstantAST* constVal;
};

class TypeDefinitionListAST : public AST {
public:
    TypeDefinitionListAST() {}
    void push_back(TypeDefinitionAST* type) { typeList.push_back(type); }
    std::vector<TypeDefinitionAST*> get_type_list() { return typeList; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    std::vector<TypeDefinitionAST*> typeList;
};

class TypeDefinitionAST : public AST {
public:
    TypeDefinitionAST() {}
    TypeDefinitionAST(std::string id, TypeDenoterAST* type)
        : id(id), type(type) {}
    std::string get_id() { return id; }
    TypeDenoterAST* get_type() { return type; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    std::string id;
    TypeDenoterAST* type;
};

class VariableDeclarationListAST : public AST {
public:
    VariableDeclarationListAST() {}
    void push_back(VariableDeclarationAST* var) {
        varList.push_back(var);
    }
    std::vector<VariableDeclarationAST*> get_var_list() { return varList; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    std::vector<VariableDeclarationAST*> varList;
};

class VariableDeclarationAST : public AST {
public:
    VariableDeclarationAST() {}
    VariableDeclarationAST(IdentifierListAST* idList, TypeDenoterAST* type)
        : idList(idList), type(type) {}
    IdentifierListAST* get_id_list() { return idList; }
    TypeDenoterAST* get_type() { return type; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    IdentifierListAST* idList;
    TypeDenoterAST* type;
};

class ProcedureFunctionDeclarationListAST : public AST {
public:
    ProcedureFunctionDeclarationListAST() {}
    void push_back(SubprogramDeclarationAST* prog) {
        subProgList.push_back(prog);
    }
    std::vector<SubprogramDeclarationAST*> get_sub_list() { return subProgList; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    std::vector<SubprogramDeclarationAST*> subProgList;
};

//// Subprogram Declarations

class SubprogramDeclarationAST : public AST {
public:
    enum Type { PROCEDURE, FUNCTION };
    SubprogramDeclarationAST() {}
    SubprogramDeclarationAST(Type t)
        : T(t) {}
    Type type() { return T; } 
    void set(Type t) { T = t; }
    void* accept(Visitor* v) override { return v->visit(this); }
private:
    Type T;
};

///// Procedure Declarations

class ProcedureDeclarationAST : public SubprogramDeclarationAST {
public:
    ProcedureDeclarationAST()
        : SubprogramDeclarationAST(SubprogramDeclarationAST::Type::PROCEDURE) {}
    ProcedureDeclarationAST(ProcedureHeadingAST* head, ProcedureBodyAST* body)
        : SubprogramDeclarationAST(SubprogramDeclarationAST::Type::PROCEDURE),
        head(head), body(body) {}
    ProcedureHeadingAST* get_head() { return head; }
    ProcedureBodyAST* get_body() { return body; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    ProcedureHeadingAST* head;
    ProcedureBodyAST* body;
};

class ProcedureHeadingAST : public AST {
public:
    ProcedureHeadingAST() {}
    ProcedureHeadingAST(std::string name, FormalParameterListAST* params)
        : name(name), params(params) {}
    std::string get_name() { return name; }
    FormalParameterListAST* get_params() { return params; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    std::string name;
    FormalParameterListAST* params;
};

class ProcedureBodyAST : public AST {
public:
    ProcedureBodyAST() {}
    ProcedureBodyAST(BlockAST* block) : block(block) {}
    BlockAST* get_block() { return block; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    BlockAST* block;
};

class TypeDenoterAST : public AST {
public:
    // Only support SIMPLE, ARRAY, RECORD
    enum Type {
        SIMPLE, ENUMERATED, SUBRANGE, ARRAY, RECORD, SET, FILE, POINTER, IDENTIFIER
    };
    TypeDenoterAST() {}
    TypeDenoterAST(Type t) : T(t) {}
    Type type() { return T; }
    void set(Type t) { T = t; }
    void* accept(Visitor* v) override { return v->visit(this); }
private:
    Type T;
};

///// Function Declarations

class FunctionDeclarationAST : public SubprogramDeclarationAST {
public:
    FunctionDeclarationAST()
        : SubprogramDeclarationAST(SubprogramDeclarationAST::Type::FUNCTION) {}
    FunctionDeclarationAST(FunctionHeadingAST* head, FunctionBodyAST* body)
        : SubprogramDeclarationAST(SubprogramDeclarationAST::Type::FUNCTION),
        head(head), body(body) {}
    FunctionHeadingAST* get_head() { return head; }
    FunctionBodyAST* get_body() { return body; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    FunctionHeadingAST* head;
    FunctionBodyAST* body;
};

class FunctionHeadingAST : public AST {
public:
    FunctionHeadingAST() {}
    FunctionHeadingAST(std::string name,
        FormalParameterListAST* params,
        SimpleTypeIdentifierAST* retType)
        : name(name), params(params), retType(retType) {}
    std::string get_name() { return name; }
    FormalParameterListAST* get_params() { return params; }
    SimpleTypeIdentifierAST* get_ret_type() { return retType; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    std::string name;
    FormalParameterListAST* params;
    SimpleTypeIdentifierAST* retType;
};

class FunctionBodyAST : public AST {
public:
    FunctionBodyAST() {}
    FunctionBodyAST(BlockAST* block) : block(block) {}
    BlockAST* get_block() { return block; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    BlockAST* block;
};

///// Formal Parameters and Arguments

class FormalParameterListAST : public AST {
public:
    FormalParameterListAST() {}
    void push_back(FormalParameterSectionAST* param) {
        params.push_back(param);
    }
    std::vector<FormalParameterSectionAST*> get_params() { return params; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    std::vector<FormalParameterSectionAST*> params;
};

class FormalParameterSectionAST : public AST {
public:
    enum Type { VALUE, VARIABLE };
    FormalParameterSectionAST() {}
    FormalParameterSectionAST(Type t) : T(t) {}
    Type type() { return T; }
    void set(Type t) { T = t; }
    void* accept(Visitor* v) override { return v->visit(this); }
private:
    Type T;
};

class ValueParameterSpecificationAST : public FormalParameterSectionAST {
public:
    ValueParameterSpecificationAST()
        : FormalParameterSectionAST(FormalParameterSectionAST::Type::VALUE) {}
    ValueParameterSpecificationAST(IdentifierListAST* idList, TypeIdentifierAST* type)
        : FormalParameterSectionAST(FormalParameterSectionAST::Type::VALUE),
        idList(idList), type(type) {}
    IdentifierListAST* get_id_list() { return idList; }
    TypeIdentifierAST* get_type() { return type; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    IdentifierListAST* idList;
    TypeIdentifierAST* type;
};

class VariableParameterSpecificationAST : public FormalParameterSectionAST {
public:
    VariableParameterSpecificationAST()
        : FormalParameterSectionAST(FormalParameterSectionAST::Type::VARIABLE) {}
    VariableParameterSpecificationAST(IdentifierListAST* idList, TypeIdentifierAST* type)
        : FormalParameterSectionAST(FormalParameterSectionAST::Type::VARIABLE),
        idList(idList), type(type) {}
    IdentifierListAST* get_id_list() { return idList; }
    TypeIdentifierAST* get_type() { return type; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    IdentifierListAST* idList;
    TypeIdentifierAST* type;
};

/// Constants ---------------------------------------------

class ConstantAST : public AST {
public:
    // If type is IDENTIFIER, val represents identifier
    // Note that const id may represent variable
    // since we don't have variable_access node
    enum Type { INTEGER, REAL, BOOLEAN, CHARACTER, STRING, IDENTIFIER };
    ConstantAST() {}
    ConstantAST(Type T, std::string val)
        : T(T), val(val) {}
    ConstantAST(Type T, std::string val, char sign)
        : T(T), val(val), s(sign) {}
    Type type() { return T; }
    std::string value() { return val; }
    char sign() { return s; }
    void set_sign(char s) { this->s = s; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    Type T;
    std::string val;
    char s; // sign
};

/// Types -------------------------------------------------

class TypeIdentifierAST : public TypeDenoterAST {
public:
    TypeIdentifierAST(TypeDenoterAST::Type t = TypeDenoterAST::Type::IDENTIFIER)
        : TypeDenoterAST(t), id("") {}
    TypeIdentifierAST(std::string id, TypeDenoterAST::Type t = TypeDenoterAST::Type::IDENTIFIER)
        : TypeDenoterAST(t), id(id) {}
    std::string get_id() { return id; }
    void* accept(Visitor* v) override { return v->visit(this); }
private:
    std::string id;
};

class SimpleTypeIdentifierAST : public TypeIdentifierAST {
public:
    enum StandardType { INTEGER, REAL, BOOLEAN, CHARACTER, STRING };
    SimpleTypeIdentifierAST()
        : TypeIdentifierAST(TypeDenoterAST::Type::SIMPLE) {}
    SimpleTypeIdentifierAST(StandardType t)
        : TypeIdentifierAST(TypeDenoterAST::Type::SIMPLE), T(t) {}
    StandardType std_type() { return T; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    StandardType T;
};

// May not support?
// class EnumeratedTypeAST : public TypeDenoterAST {}
// class SubrangeTypeAST : public TypeDenoterAST {}

class ArrayTypeAST : public TypeDenoterAST {
public:
    ArrayTypeAST()
        : TypeDenoterAST(TypeDenoterAST::Type::ARRAY) {}
    ArrayTypeAST(PeriodListAST* perList, TypeDenoterAST* type)
        : TypeDenoterAST(TypeDenoterAST::Type::ARRAY),
        perList(perList), type(type) {}
    PeriodListAST* get_per_list() { return perList; }
    TypeDenoterAST* get_type() { return type; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    PeriodListAST* perList;
    TypeDenoterAST* type;
};

class PeriodListAST : public AST {
public:
    PeriodListAST() {}
    void push_back(PeriodAST* per) { perList.push_back(per); }
    std::vector<PeriodAST*> get_per_list() { return perList; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    std::vector<PeriodAST*> perList;
};

class PeriodAST : public AST {
public:
    PeriodAST() {}
    PeriodAST(ConstantAST* beg, ConstantAST* end)
        : beg(beg), end(end) {}
    ConstantAST* get_beg() { return beg; }
    ConstantAST* get_end() { return end; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    ConstantAST* beg;
    ConstantAST* end;
};

class RecordTypeAST : public TypeDenoterAST {
public:
    RecordTypeAST()
        : TypeDenoterAST(TypeDenoterAST::Type::RECORD) {}
    RecordTypeAST(FieldListAST* fieldList)
        : TypeDenoterAST(TypeDenoterAST::Type::RECORD),
        fieldList(fieldList) {}
    FieldListAST* get_field_list() { return fieldList; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    FieldListAST* fieldList;
};

class FieldListAST : public AST {
public:
    FieldListAST() {}
    void push_back(FieldAST* field) { fieldList.push_back(field); }
    std::vector<FieldAST*> get_field_list() { return fieldList; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    std::vector<FieldAST*> fieldList;
};

class FieldAST : public AST {
public:
    FieldAST() {}
    FieldAST(IdentifierListAST* idList, TypeDenoterAST* type)
        : idList(idList), type(type) {}
    IdentifierListAST* get_id_list() { return idList; }
    TypeDenoterAST* get_type() { return type; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    IdentifierListAST* idList;
    TypeDenoterAST* type;
};

/// Statements --------------------------------------------

// Base for stmt
class StatementAST : public AST {
public:
    enum StmtType {
        PROCEDURE, COMPOUND, EMPTY, ASSIGN, IF, CASE, REPEAT, WHILE, FOR
    };
    StatementAST() {}
    StatementAST(StmtType t) : T(t) {}
    StmtType type() { return T; }
    void* accept(Visitor* v) override { return v->visit(this); }
private:
	StmtType T;
};

class ProcedureStatementAST : public StatementAST {
public:
    ProcedureStatementAST()
        : StatementAST(StatementAST::StmtType::PROCEDURE) {}
	ProcedureStatementAST(std::string proName,
        ActualParameterListAST* paramList)
		: StatementAST(StatementAST::StmtType::PROCEDURE),
        proName(proName), paramList(paramList) {}
    std::string get_pro_name() { return proName; }
    ActualParameterListAST* get_param_list() { return paramList; }
    void* accept(Visitor* v) override { return v->visit(this); }
private:
	std::string proName;
	ActualParameterListAST* paramList;
};

class IOProcedureStatementAST : public ProcedureStatementAST {
public:
    IOProcedureStatementAST(std::string proName,
        ActualParameterListAST* paramList)
        : ProcedureStatementAST(proName, paramList) {}
    void* accept(Visitor* v) final { return v->visit(this); }
};

class ActualParameterListAST : public AST {
public:
	ActualParameterListAST() {}
	void push_back(ActualParameterAST* param) {
		actualParamList.push_back(param);
	}
    std::vector<ActualParameterAST*> get_actual_param_list() { return actualParamList; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
	std::vector<ActualParameterAST*> actualParamList;
};

class ReadParameterListAST : public AST {
public:
    ReadParameterListAST() {}
    void* accept(Visitor* v) final { return v->visit(this); }
};

class WriteParameterList : public AST {
public:
    WriteParameterList() {}
    void* accept(Visitor* v) final { return v->visit(this); }
};

class ActualParameterAST : public AST {
public:
    ActualParameterAST() {}
	ActualParameterAST(ExpressionAST* expression)
		: expression(expression) {}
    ExpressionAST* get_expression() { return expression; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
	ExpressionAST* expression;
};

class StatementPartAST : public AST {
public:
    StatementPartAST() {}
	StatementPartAST(CompoundStatement* compoundStatement)
		: compoundStatement(compoundStatement) {}
    CompoundStatement* get_compound_statement() { return compoundStatement; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
	CompoundStatement* compoundStatement;
};

class CompoundStatement : public StatementAST {
public:
    CompoundStatement()
        : StatementAST(StatementAST::StmtType::COMPOUND) {}
	CompoundStatement(StatementSequenceAST* statementList)
		: StatementAST(StatementAST::StmtType::COMPOUND),
        statementList(statementList) {}
    StatementSequenceAST* get_statement_list() { return statementList; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
	StatementSequenceAST* statementList;
};

class StatementSequenceAST : public AST {
public:
    StatementSequenceAST() {}
	void push_back(StatementAST* add) {
		statementList.push_back(add);
	}
    std::vector<StatementAST*> get_statement_list() { return statementList; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
	std::vector<StatementAST*> statementList;
};

class EmptyStatementAST : public StatementAST {
public:
    EmptyStatementAST()
        : StatementAST(StatementAST::StmtType::EMPTY) {}
    void* accept(Visitor* v) final { return v->visit(this); }
};

class AssignmentStatementAST : public StatementAST {
public:
    AssignmentStatementAST()
        : StatementAST(StatementAST::StmtType::ASSIGN) {}
	AssignmentStatementAST(ExpressionAST* lhs, ExpressionAST* rhs)
		: StatementAST(StatementAST::StmtType::ASSIGN),
        lhs(lhs), rhs(rhs) {}
    ExpressionAST* get_lhs() { return lhs; }
    ExpressionAST* get_rhs() { return rhs; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
	ExpressionAST* lhs;
	ExpressionAST* rhs;
};

// Not support in Pascal-S
// class WithStatementAST : public StatementAST {}

class IfStatementAST : public StatementAST {
public:
    IfStatementAST()
        : StatementAST(StatementAST::StmtType::IF) {}
	IfStatementAST(ExpressionAST* expression,
        StatementAST* thenStatement,
        StatementAST* elseStatement)
		: StatementAST(StatementAST::StmtType::IF),
        expression(expression),
        thenStatement(thenStatement),
        elseStateMent(elseStatement) {}
    ExpressionAST* get_expression() { return expression; }
    StatementAST* get_then_statement() { return thenStatement; }
    StatementAST* get_else_statement() { return elseStateMent; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
	ExpressionAST* expression;
	StatementAST* thenStatement;
	StatementAST* elseStateMent;
};

class CaseStatementAST : public StatementAST {
public:
    CaseStatementAST()
        : StatementAST(StatementAST::StmtType::CASE) {}
	CaseStatementAST(ExpressionAST* expr,
        CaseListElementListAST* caseBody)
		: StatementAST(StatementAST::StmtType::CASE),
        expression(expr), caseBody(caseBody) {}
    ExpressionAST* get_expression() { return expression; }
    CaseListElementListAST* get_case_body() { return caseBody; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
	ExpressionAST* expression;
	CaseListElementListAST* caseBody;
};

class RepeatStatementAST : public StatementAST {
public:
    RepeatStatementAST()
        : StatementAST(StatementAST::StmtType::REPEAT) {}
	RepeatStatementAST(StatementSequenceAST* statementList,
        ExpressionAST* expression)
		: StatementAST(StatementAST::StmtType::REPEAT),
        statementList(statementList),
        expression(expression) {}
    StatementSequenceAST* get_statement_list() { return statementList; }
    ExpressionAST* get_expression() { return expression; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
	StatementSequenceAST* statementList;
	ExpressionAST* expression;
};

class WhileStatementAST : public StatementAST {
public:
    WhileStatementAST()
        : StatementAST(StatementAST::StmtType::WHILE) {}
	WhileStatementAST(ExpressionAST* expression, StatementAST* statement)
		: StatementAST(StatementAST::StmtType::WHILE),
        expression(expression), statement(statement) {}
    ExpressionAST* get_expression() { return expression; }
    StatementAST* get_statement(){ return statement; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
	ExpressionAST* expression;
	StatementAST* statement;
};

class ForStatementAST : public StatementAST {
public:
    enum ForDirection { OP_TO, OP_DOWNTO };
    ForStatementAST()
        : StatementAST(StatementAST::StmtType::FOR) {}
	ForStatementAST(IdentifierAST* id,
        ExpressionAST* initialValue,
        ForDirection direction,
		ExpressionAST* endValue,
        StatementAST* statement)
		: StatementAST(StatementAST::StmtType::FOR),
        id(id),
        initialValue(initialValue),
        direction(direction),
        endValue(endValue),
        statement(statement) {}

    IdentifierAST* get_id(){ return id; }
    ExpressionAST* get_initial_value() { return  initialValue; }
    ForDirection get_direction() { return direction; }
    ExpressionAST* get_endvalue() { return endValue; }
    StatementAST* get_statement() { return statement; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
	IdentifierAST* id;
	ExpressionAST* initialValue;
	ForDirection direction;
	ExpressionAST* endValue;
	StatementAST* statement;
};

class CaseListElementListAST : public AST {
public:
    CaseListElementListAST() {}
    void push_back(CaseListElementAST* branch) {
        branchList.push_back(branch);
    }
    std::vector<CaseListElementAST*> get_banch_list() { return branchList; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    std::vector<CaseListElementAST*> branchList;
};

class CaseListElementAST : public AST {
public:
    CaseListElementAST() {}
    CaseListElementAST(CaseConstantListAST* constList, StatementAST* statement)
        : constList(constList), statement(statement) {}
    CaseConstantListAST* get_const_list() { return constList; }
    StatementAST* get_statement() { return statement; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    CaseConstantListAST* constList;
    StatementAST* statement;
};

class CaseConstantListAST : public AST {
public:
    CaseConstantListAST() {}
    void push_back(ConstantAST* constVal) {
        constList.push_back(constVal);
    }
    std::vector<ConstantAST*> get_const_list() { return constList; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    std::vector<ConstantAST*> constList;
};

/// Expressions and Variables -----------------------------

class ExpressionAST : public AST {
public:
    enum Type {
        CONSTANT, IDENTIFIER, BINARY, INDEXED, FIELD, FUNCTION
    };
    ExpressionAST() {}
    ExpressionAST(Type t) : T(t) {}
    Type type() { return T; }
    void* accept(Visitor* v) override { return v->visit(this); }
private:
    Type T;
};

class UnsignedConstantAST : public ExpressionAST {
public:
    UnsignedConstantAST()
        : ExpressionAST(ExpressionAST::Type::CONSTANT) {}
    UnsignedConstantAST(ConstantAST* val)
        : ExpressionAST(ExpressionAST::Type::CONSTANT), val(val) {}
    ConstantAST* get_value() { return val; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    ConstantAST* val;
};

class IdentifierAST : public ExpressionAST {
public:
    IdentifierAST()
        : ExpressionAST(ExpressionAST::Type::IDENTIFIER) {}
	IdentifierAST(std::string name)
        : ExpressionAST(ExpressionAST::Type::IDENTIFIER), name(name) {}
    std::string get_name() { return name; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
	std::string name;
};

class BinaryAST : public ExpressionAST {
public:
    enum OpType {
        OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_MOD, OP_INC, OP_DEC,
        OP_EQ, OP_NE, OP_LT, OP_LE, OP_GT, OP_GE,
        OP_AND, OP_OR, OP_NOT
    };
    BinaryAST()
        : ExpressionAST(ExpressionAST::Type::BINARY) {}
	BinaryAST(ExpressionAST* l, ExpressionAST* r, OpType t)
		: ExpressionAST(ExpressionAST::Type::BINARY),
        lhs(l), rhs(r), op(t) {}
    OpType type() { return op; }
	void set(OpType t) { op = t; }
    ExpressionAST* get_lhs() { return lhs; }
    ExpressionAST* get_rhs() { return rhs; }
    OpType get_op() { return op; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
	ExpressionAST* lhs;
	ExpressionAST* rhs;
    OpType op;
};

class IndexedVariableAST : public ExpressionAST {
public:
    IndexedVariableAST()
        : ExpressionAST(ExpressionAST::Type::INDEXED) {}
	IndexedVariableAST(std::string arrayName, IndexExpressionListAST* indices)
		: ExpressionAST(ExpressionAST::Type::INDEXED),
        arrayName(arrayName), indices(indices) {}
    std::string get_array_name() { return arrayName; }
    IndexExpressionListAST* get_indices() { return indices; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
	std::string arrayName;
	IndexExpressionListAST* indices;
};

class IndexExpressionListAST : public AST {
public:
    IndexExpressionListAST() {}
    void push_back(IndexExpressionAST* expr) {
        indices.push_back(expr);
    }
    std::vector<IndexExpressionAST*> get_indices() { return indices; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    std::vector<IndexExpressionAST*> indices;
};

class IndexExpressionAST : public AST {
public:
    IndexExpressionAST() {}
    IndexExpressionAST(ExpressionAST* expression)
        : expression(expression) {}
    ExpressionAST* get_expression() { return expression; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
    ExpressionAST* expression;
};

class FieldDesignatorAST : public ExpressionAST {
public:
    FieldDesignatorAST()
        : ExpressionAST(ExpressionAST::Type::FIELD) {}
	FieldDesignatorAST(std::string recordName, std::string member)
		: ExpressionAST(ExpressionAST::Type::FIELD),
        recordName(recordName), member(member) {}
    std::string get_record_name() { return recordName; }
    std::string get_member() { return member; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
	std::string recordName;
	std::string member;
};

class FunctionDesignatorAST : public ExpressionAST {
public:
    FunctionDesignatorAST()
        : ExpressionAST(ExpressionAST::Type::FUNCTION) {}
    FunctionDesignatorAST(std::string funcName, ActualParameterListAST* paramList)
        : ExpressionAST(ExpressionAST::Type::FUNCTION),
        funcName(funcName), paramList(paramList) {}
    std::string get_func_name() { return funcName; }
    ActualParameterListAST* get_param_list() { return paramList; }
    void* accept(Visitor* v) final { return v->visit(this); }
private:
	std::string funcName;
	ActualParameterListAST* paramList;
};

#endif
